package com.gitee.huanminabc.utils_tools.fast_cache;

import lombok.extern.slf4j.Slf4j;
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.HTreeMap;
import org.mapdb.Serializer;

import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 通过内存映射技术可以做到比直接操作文件快几倍的速度, 从目前体验来说
 * https://jankotek.gitbooks.io/mapdb/content/performance/  优化
 */
@Slf4j
public class MapDBUtil {
    private static final String tempName = "java_local_cache";
    private static final File stopFile = new File(getTempPath() + File.separator + "cache");
    //本地缓存文件地址
    private static String getTempPath() {
        return System.getProperty("java.io.tmpdir") + File.separator + tempName;
    }
    static {
        String tempPath = getTempPath();
        //缓存目录不存在则创建
        File tempDir = new File(tempPath);
        if (!tempDir.exists()) {
            tempDir.mkdirs();
        }

        //读取目录下所有缓存文件
        File[] files = tempDir.listFiles();
        if (files != null) {
            //删除最近7天之前没有修改的的缓存文件,避免缓存文件过多占用磁盘空间
            List<File> collect = Arrays.stream(files).filter(file -> {
                long time = System.currentTimeMillis() - file.lastModified();
                return time > 1000 * 60 * 60 * 24 * 7;
            }).collect(Collectors.toList());
            collect.forEach(File::delete);
        }
    }
    //每次创建一个新的数据库,key提升性能,如果都在一个数据库中,那么会导致数据库文件过大,性能下降
    public static DB defaultCreate(String key) {
        String filePath= getTempPath() + File.separator + key;
        log.info(key+"本地缓存文件地址:{}",filePath);
        DBMaker.Maker maker = DBMaker
                .fileDB(filePath)
                .fileChannelEnable()//使用 FileChannel 代替 RandomAccessFile 可能会提高性能
                .fileMmapEnable() //使用内存映射文件, 会提高性能
                .fileMmapEnableIfSupported()
                .fileMmapPreclearDisable()
                .allocateStartSize( 1024*1024*10)  // 初始大小10mb
                //同步、刷新和关闭等操作必须遍历所有缓冲区。因此，增量大小越大可以加快提交和关闭操作的速度。
                .allocateIncrement(1024*1024*10)
                .cleanerHackEnable() //使用额外的线程清理内存映射, 但是会增加性能开销
                .closeOnJvmShutdown(); //关闭JVM时自动关闭数据库, 所以就算忘记关闭数据库也不会有问题

        DB make = null;
        try {
            make = maker.make();
        } catch (Exception e) {
            log.warn("错误:",e);
            //删除文件
            stopFile.delete();
            make = maker.make();
        }
        //文件内容加载到磁盘缓存中, 这样key减少io操作
        make.getStore().fileLoad();
        return make;
    }


    //创建HTreeMap
    public static <K, V> org.mapdb.HTreeMap<K, V> createHTreeMap(DB db, String name) {
        HTreeMap orOpen = db.hashMap(name).
                keySerializer(Serializer.STRING).
                valueSerializer(Serializer.JAVA).
                expireStoreSize(1024L * 1024 * 1024*4). //最大缓存4G,超过了就会删除最早访问的
                expireAfterGet().
                createOrOpen();
        return orOpen;
    }


}
